'Andrew's Functions
' $HeadURL: http://gabbscript.googlecode.com/svn/branches/funcbranch/Gabbscript/AndrewFunctions.vb $
' $Author: andrewshen123 $
' $Revision: 81 $
' $Date: 2007-11-06 19:38:33 +0000 (Tue, 06 Nov 2007) $

Imports System.IO

Module AndrewFunctions

    ' An table containing all open files used by FILE I/O FUNCTIONS
    Dim openFiles As Hashtable = New Hashtable()

    ' Register the functions
    Sub RegisterFunctions(ByRef funcList As FunctionList)
        funcList.RegisterFunction("IF", AddressOf IF_)
        funcList.RegisterFunction("FOR", AddressOf FOR_)
        funcList.RegisterFunction("LOOP", AddressOf LOOP_)
        funcList.RegisterFunction("FOPEN", AddressOf FOPEN)
        funcList.RegisterFunction("FCLOSE", AddressOf FCLOSE)
        funcList.RegisterFunction("FWRITE", AddressOf FWRITE)
        funcList.RegisterFunction("FPEEK", AddressOf FPEEK)
        funcList.RegisterFunction("FREAD", AddressOf FREAD)
        funcList.RegisterFunction("FREADL", AddressOf FREADL)
        funcList.RegisterFunction("FFLUSH", AddressOf FFLUSH)
        funcList.RegisterFunction("SET", AddressOf SETVAR)
    End Sub

    ' PROGRAM FLOW

    ' IF: Conditional branch
    Function IF_(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct number of arguments
        If args.Count <> 5 Then Return False

        ' Declarations
        Dim val1 As Argument = args.Item(0)             ' The first value to compare
        Dim op As String = args.Item(1).Data            ' The operator to compare the values with (i.e. ">", "=" or "<"
        Dim val2 As Argument = args.Item(2)             ' The second value to compare
        Dim ifTrue As Integer = args.Item(3).Data       ' Where to jump if the comparison returns true
        Dim ifFalse As Integer = args.Item(4).Data      ' Where to jump if the comparison returns false

        ' Calls the comparison function to compare the two values
        If script.Compare(val1, op, val2) Then
            ' If true, jumps to ifTrue
            script.SetInstructionPointer(ifTrue)
        Else
            ' If false, jumps to isFalse
            script.SetInstructionPointer(ifFalse)
        End If

        ' Function succeeded! Yay!
        Return True

    End Function
    ' FOR: For loop
    Function FOR_(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct number of arguments
        If args.Count <> 5 Then Return False

        ' Declarations
        Dim var As Argument = args.Item(0)                  ' The counter
        Dim lim As Double = args.Item(1).Data               ' The value the counter is supposed to reach
        Dim inc As Double = args.Item(2).Data               ' The increment to raise the counter by
        Dim blockStart As Integer = args.Item(3).Data       ' Where the looped code starts
        Dim blockEnd As Integer = args.Item(4).Data + 1     ' Where the looped code ends. Add 1 because the instruction pointer is 0 based (starts at 0 instead of 1)

        ' Checks for infinite loop
        If inc > 0 And lim < var.Data Or inc < 0 And lim > var.Data Or inc = 0 Then Return False

        If var.Data <> lim Then
            ' If the current value of var does not equal lim, jumps to blockStart
            script.SetInstructionPointer(blockStart)
            ' Then increments the counter
            var.Data += inc
        Else
            ' If it does equal, jump to the line after the looped block
            script.SetInstructionPointer(blockEnd)
        End If

        ' Function succeeded! Yay!
        Return True

    End Function
    ' LOOP: While/until loops
    Function LOOP_(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct number of arguments
        If args.Count <> 5 Then Return False

        ' Declarations
        Dim loopFrom As Integer = args.Item(0).Data     ' Where to loop from
        Dim val1 As Argument = args.Item(1)             ' The first value to compare
        Dim op As String = args.Item(2).Data            ' The operator to compare with
        Dim val2 As Argument = args.Item(3)             ' The second value to compare
        Dim cond As Char = args.Item(4).Data            ' Either "w" for While, or "u" for Until

        Select Case cond
            Case "w"
                ' If it's a While loop, check if the comparison returns true
                If script.Compare(val1, op, val2) Then
                    ' If so, jump to loopFrom
                    script.SetInstructionPointer(loopFrom)
                End If
            Case "u"
                ' If it's an Until loop, check if the comparison returns false
                If Not script.Compare(val1, op, val2) Then
                    ' If so, jump to loopFrom
                    script.SetInstructionPointer(loopFrom)
                End If
            Case Else
                ' If the condition is anything else, complain
                Return False
        End Select

        ' Function succeeded! Yay!
        Return True

    End Function

    ' FILE I/O FUNCTIONS

    ' FOPEN: Opens file to read/write to
    Function FOPEN(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct number of arguments
        If args.Count <> 2 Then Return False

        ' Declarations
        Dim filename As String = args.Item(0).Data  ' The name of the file to open
        Dim type As Char = args.Item(1).Data        ' Either "w" for Write, or "r" for Read

        Select Case type
            Case "w"
                ' If Write, open the file for writing
                Dim file As StreamWriter = New StreamWriter(filename)
                ' Then add it to the table of open files
                openFiles.Add(filename, file)
            Case "r"
                ' If Read, open the file for reading
                Dim file As StreamReader = New StreamReader(filename)
                ' Then add it to the table of open files
                openFiles.Add(filename, file)
            Case Else
                ' If the type is anything else, complain
                Return False
        End Select

        ' Function succeeded! Yay!
        Return True

    End Function
    ' FCLOSE: Closes the stream (and flushes the buffer if necessary)
    Function FCLOSE(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct number of arguments
        If args.Count <> 1 Then Return False

        ' Declaration
        Dim file As String = args.Item(0).Data  ' The file to close

        ' Checks if the file exists in the table
        If openFiles.Contains(file) Then
            ' Closes the file
            openFiles.Item(file).close()
            ' Then removes it from the list
            openFiles.Remove(file)
        Else
            ' If not, complain
            Return False
        End If

        ' Function succeeded!(Yay!)
        Return True

    End Function
    ' FWRITE: Writes to a stream
    Function FWRITE(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct range of arguments
        If args.Count < 2 Then Return False

        ' Declarations
        Dim file As String = args.Item(0).Data  ' The file to write to
        Dim i As Integer                        ' A counter for the loop

        ' Checks if the file exists in the table, and is a StreamWriter, because if it were a StreamReader, the computer would get angry
        If openFiles.Contains(file) And openFiles.Item(file).GetType.ToString() = "System.IO+StreamWriter" Then
            For i = 1 To args.Count() - 1
                ' Writes the value of each argument to a seperate line in the stream
                openFiles.Item(file).WriteLine(args.Item(i).Data)
            Next
            ' Function succeeded! Yay!
            Return True
        End If

        ' Otherwise, complain
        Return False

    End Function
    ' FPEEK: Gets the next character in a stream, but does not consume it
    Function FPEEK(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct number of arguments
        If args.Count <> 2 Then Return False

        ' Declaration
        Dim file As String = args.Item(0).Data  ' The file to read from

        ' Checks if the file exists in the table, and is a StreamReader, because if it were a StreamWriter, the computer would get angry
        If openFiles.Contains(file) Then
            ' If so, set the value of the variable in the second argument to the next character in the file, but does not advance the pointer of the stream
            args.Item(1).Data = openFiles.Item(file).Peek
            ' Function succeeded! Yay!
            Return True
        End If

        ' Otherwise, complain
        Return False

    End Function
    ' FREAD: Gets the next character in a stream
    Function FREAD(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct number of arguments
        If args.Count <> 2 Then Return False

        ' Declaration
        Dim file As String = args.Item(0).Data  ' The file to read from

        ' Checks if the file exists in the table, and is a StreamReader, because if it were a StreamWriter, the computer would get angry
        If openFiles.Contains(file) Then
            ' If so, set the value of the variable in the second argument to the next character in the file
            args.Item(1).Data = openFiles.Item(file).Read
            ' Function succeeded! Yay!
            Return True
        End If

        ' Otherwise, complain
        Return False

    End Function
    ' FREADL: Gets the next line in a stream
    Function FREADL(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct number of arguments
        If args.Count <> 2 Then Return False

        ' Declaration
        Dim file As String = args.Item(0).Data  ' The file to read from

        ' Checks if the file exists in the table, and is a StreamReader, because if it were a StreamWriter, the computer would get angry
        If openFiles.Contains(file) Then
            ' If so, set the value of the variable in the second argument to the next line in the file
            args.Item(1).Data = openFiles.Item(file).ReadLine
            ' Function succeeded! Yay!
            Return True
        End If

        ' Otherwise, complain
        Return False

    End Function
    ' FFLUSH: Flushes a stream
    Function FFLUSH(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct number of arguments
        If args.Count <> 1 Then Return False

        ' Declarations
        Dim stream As String = args.Item(0).Data  ' The stream to flush. Heh. Flush. Like a toilet.

        ' Checks if the stream exists in the table, and is a StreamWriter, because if it were a StreamReader, the computer would get angry
        If openFiles.Contains(stream) And openFiles.Item(stream).GetType.ToString() = "System.IO+StreamWriter" Then
            ' If true, flush the stream
            openFiles.Item(stream).flush()
            ' Function succeeded! Yay!
            Return True
        End If

        ' Otherwise, complain
        Return False

    End Function
    ' SETVAR: Sets a variable
    Function SETVAR(ByVal args As ArgList, ByRef script As Script) As Boolean

        ' Checks for the correct number of arguments
        If args.Count <> 2 Then Return False

        ' Set the value of the first argument to that of the second
        args.Item(0).Data = args.Item(1).Data

        ' Function succeeded! Yay!
        Return True

    End Function

End Module